﻿namespace SharePointCSVImporter
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Xml.Linq;

    using CSVParser;

    using Microsoft.SharePoint.Client;

    public class ListItemUpdater : IDisposable
    {
        #region Fields

        public List<ListItem> existingItems;
        public Dictionary<int, XElement> itemToDeleteMethods;

        private const int itemLimit = 5000;
        const int limitPerBatch = 150; //actually is 160 according to microsoft

        SharePoint.ListsService.ListsSoapClient client;
        int currentDeleteCounter = 0;
        ListItemCollection currentItems;
        int currentPage;
        SharePoint.ListsService.ListsSoapClient deleteClient;
        bool disposeContext = false;
        int dupAction = 0;
        Action<string> ErrorAction;
        int existingItemCount;
        ExternalListLookupWorker externalListWorker;
        FieldCollection fields;
        Action<Logger.Logger> FinializeProcess;
        ListItemCollectionPosition itemPosition;
        int itemToDeleteKey = 0;
        private string listName;
        ListCollection lists;
        Logger.Logger logger;
        Task loggingTask;
        string m_webUrl;
        private int numberOfBatches = 0;
        int pageCount;
        List<XElement> pendingBatchUpdates;
        bool processCompleteCalled = false;
        private Action<string, int> progressUpdate;
        Action<List<Microsoft.SharePoint.Client.Field>> ReceivedFieldsAction;
        Action<List<Microsoft.SharePoint.Client.List>> ReceiveListsAction;
        List<List<KeyValuePair<int, XElement>>> splitDeletes;
        List targetList;
        ClientContext thisContext;
        int totalDeleteBatches;
        Action<bool, string> UpdateCompletedAction;
        int updateIndex = 0;
        Dictionary<int, List<MappedValue>> _mappedData;
        private bool _overwrite;

        #endregion Fields

        #region Constructors

        public ListItemUpdater(string webUrl, bool getContext = true)
        {
            m_webUrl = webUrl;
            logger = new Logger.Logger();
            if (getContext)
            {
                GetContext(webUrl);
            }
        }

        #endregion Constructors

        #region Properties

        public string ListName
        {
            get { return listName; }
            set { listName = value; }
        }

        #endregion Properties

        #region Methods

        public static List<List<KeyValuePair<int, List<MappedValue>>>> SplitUpdates(Dictionary<int, List<MappedValue>> data, int nSize = 151)
        {
            List<List<KeyValuePair<int, List<MappedValue>>>> list = new List<List<KeyValuePair<int, List<MappedValue>>>>();
            if (data.Count() <= nSize)
            {
                List<KeyValuePair<int, List<MappedValue>>> segment = (from c in data
                                                                      where c.Key > -1
                                                                      select c).ToList();
                list.Add(segment);
            }
            else
            {
                for (int i = -1; i <= data.Count; i += nSize)
                {
                    int endSize = Math.Min(i + nSize, data.Count);
                    List<KeyValuePair<int, List<MappedValue>>> segment = (from c in data
                                                                          where c.Key > i && c.Key <= endSize
                                                                          select c).ToList();
                    if (segment != null && segment.Count > 0)
                    {
                        list.Add(segment);
                    }
                }

            }
            //var total = 0;
            //foreach (var ls in list)
            //{
            //    total += ls.Count;
            //}
            return list;
        }

        public void Dispose()
        {
            if (disposeContext && thisContext != null)
            {
                thisContext.Dispose();
            }
        }

        public bool RecordExists(List<MappedValue> record, out int[] existingIds)
        {
            bool rvalue = false;
            existingIds = null;
            if (existingItems != null && existingItems.Any())
            {
                List<MappedValue> keyValues = (from c in record
                                               where c.FieldMapping.IsKeyColumn
                                               select c).ToList();
                List<ListItem> potentialMatches = existingItems.ToList();
                if (keyValues.Any())
                {

                    for (var i = 0; i <= keyValues.Count; i++)
                    {
                        if (i == keyValues.Count)
                        {
                            if (potentialMatches.Count.Equals(1))
                            {
                                rvalue = true;
                                existingIds = potentialMatches.Select(c => c.Id).ToArray();
                                break;
                            }
                            else
                            {
                                if (potentialMatches.Count > 1)
                                {
                                    rvalue = true;
                                    //make sure the latest is at index 0
                                    existingIds = potentialMatches.OrderByDescending(c => c.FieldValues["Created"]).Select(c => c.Id).ToArray();
                                }
                            }
                        }
                        else
                        {
                            MappedValue keyValue = keyValues[i];
                            string destinationColumn = keyValue.FieldMapping.DestinationColumn;
                            object value = keyValue.Value;
                            string columnType = keyValue.FieldMapping.DestinationColumnType;
                            potentialMatches = potentialMatches.Where(c => keyValueExists(c, destinationColumn, columnType, value)).ToList();
                            if (potentialMatches == null || potentialMatches.Count.Equals(0))
                            {
                                break;
                            }
                        }
                    }
                }
            }
            return rvalue;
        }

        public List<List<KeyValuePair<int, XElement>>> SplitDeleteOperations(List<KeyValuePair<int, XElement>> data, int nSize)
        {
            List<List<KeyValuePair<int, XElement>>> rvalue = new List<List<KeyValuePair<int, XElement>>>();
            if (data.Count() <= nSize)
            {
                List<KeyValuePair<int, XElement>> segment = (from c in data
                                                             where c.Key > -1
                                                             select c).ToList();
            }
            else
            {
                for (int i = -1; i <= data.Count; i += nSize)
                {
                    int endSize = Math.Min(i + nSize, data.Count);
                    List<KeyValuePair<int, XElement>> segment = (from c in data
                                                                 where c.Key > i && c.Key <= endSize
                                                                 select c).ToList();
                    if (segment != null && segment.Count > 0)
                    {
                        rvalue.Add(segment);
                    }
                }

            }
            return rvalue;
        }

        /// <summary>
        /// If calling overwrite, 
        /// </summary>
        /// <param name="mappedData">Dictionary of row index and column/field mappings</param>
        /// <param name="webUrl"></param>
        /// <param name="listName"></param>
        /// <param name="overwrite"></param>
        /// <returns></returns>
        public void UpdateFromCsV(Dictionary<int, List<MappedValue>> mappedData, string listName, bool overwrite, int duplicateAction, Action<string, int> progressUpdate, Action<string> errorNotification, Action<bool, string> finishedcallback, Action<Logger.Logger> processCompleted)
        {
            this.UpdateCompletedAction = finishedcallback;
            this.FinializeProcess = processCompleted;
            this.ErrorAction = errorNotification;
            this.progressUpdate = progressUpdate;
            this.listName = listName;
            this.dupAction = duplicateAction;
            this._mappedData = mappedData;
            this._overwrite = overwrite;
            if (!string.IsNullOrEmpty(listName) && mappedData != null)
            {
                bool externalLookupExists = (from c in mappedData
                                             from d in c.Value
                                             where d.FieldMapping.Transformation != null && d.FieldMapping.Transformation.TransformationType.Equals(TransformationType.ExternalLookupReplace)
                                             select d).Any();

                if (externalLookupExists)
                {

                    GetExternalLookupItemsThenContinue();
                }
                else
                {
                    StartStep2();
                }
            }
        }

        public void UpdateItemsToDeleteQueue(int itemId)
        {
            if (itemId > 0)
            {
                if (itemToDeleteMethods == null)
                {
                    itemToDeleteMethods = new Dictionary<int, XElement>();
                }
                if (!itemToDeleteMethods.ContainsKey(itemId))
                {
                    itemToDeleteMethods.Add(itemToDeleteKey, CreateDeleteMethod(itemId));
                    itemToDeleteKey++;
                }
            }
        }

        internal void ExternalLookupItemsReceived(List<ListItem> items, string errorMessage)
        {
            if (items != null)
            {
                StartStep2();
            }
            else
            {
                MessageBox.Show(errorMessage);
            }
        }

        internal void GetListFields(Action<List<Microsoft.SharePoint.Client.Field>> successFunction, string listName)
        {
            this.ReceivedFieldsAction = successFunction;
            Web web = thisContext.Web;
            ListCollection lists = web.Lists;
            List targetList = lists.GetByTitle(listName);
            fields = targetList.Fields;
            thisContext.Load(fields);
            thisContext.ExecuteQueryAsync(OnGetFieldsSucceeded, OnGetListFailed);
        }

        internal void GetLists(Action<List<Microsoft.SharePoint.Client.List>> successFunction)
        {
            Web web = thisContext.Web;
            lists = web.Lists;
            thisContext.Load(lists);
            this.ReceiveListsAction = successFunction;
            thisContext.ExecuteQueryAsync(OnGetListSucceeded, OnGetListFailed);
        }

        void client_UpdateListItemsCompleted(object sender, SharePoint.ListsService.UpdateListItemsCompletedEventArgs e)
        {
            //60 % complete when process starts go to 90% so 30 percent left
            //determine if another batch of updates is needed
            //do something with results

            SearchCatchErrors(e.Result, e.Error, pendingBatchUpdates[updateIndex]);

            updateIndex++;
            int pendingUpdates = pendingBatchUpdates.Count;
            if (updateIndex < pendingUpdates)
            {
                double perc = (double)updateIndex / (double)pendingUpdates;
                perc = Math.Round(perc, 2);
                int newPercentComplete = (int)Math.Ceiling(60 + (perc * 30));

                progressUpdate("Uploading CSV data to SharePoint List, " + this.listName + " " + perc.ToString("P"), newPercentComplete);
                UpdateTarget(pendingBatchUpdates[updateIndex]);
            }
            else
            {
                if (dupAction.Equals(2) && itemToDeleteMethods != null && itemToDeleteMethods.Count > 0)
                {
                    logger.ItemsDeleted = itemToDeleteMethods.Count;
                    StartDeletesThenFinish();
                }
                else
                {
                    ProcessComplete(true, "CSV File Import Completed");
                }
            }
        }

        private XElement CreateDeleteMethod(int itemId)
        {
            if (itemToDeleteMethods != null)
            {
                int opId = itemToDeleteMethods.Count + 1;
                XElement recordElement = new XElement("Method");
                recordElement.SetAttributeValue("Cmd", "Delete");
                recordElement.SetAttributeValue("ID", opId);

                XElement idFieldElement = new XElement("Field");
                idFieldElement.SetAttributeValue("Name", "ID");
                idFieldElement.SetValue(itemId);
                recordElement.Add(idFieldElement);
                return recordElement;
            }
            else
            {
                return null;
            }
        }

        private List<XElement> CreateInsertOnlyBody(List<List<KeyValuePair<int, List<MappedValue>>>> updateGroups)
        {
            List<XElement> rvalue = new List<XElement>();
            foreach (var update in updateGroups)
            {
                rvalue.Add(GetUpdateXml(update, false, 0));
            }
            return rvalue;
        }

        private SharePoint.ListsService.ListsSoapClient CreateSoapClient()
        {
            string address = m_webUrl;
            if (!m_webUrl.EndsWith("/"))
            {
                address = address + "/";
            }
            address = address + "_vti_bin/lists.asmx";
            System.ServiceModel.BasicHttpSecurityMode mode;
            if (address.StartsWith("https"))
            {
                mode = System.ServiceModel.BasicHttpSecurityMode.Transport;
            }
            else
            {
                mode = System.ServiceModel.BasicHttpSecurityMode.None;
            }
            System.ServiceModel.BasicHttpBinding httpBinding = new System.ServiceModel.BasicHttpBinding(mode);
            httpBinding.MaxReceivedMessageSize = 2147483647;
            httpBinding.CloseTimeout = TimeSpan.FromMinutes(5);
            httpBinding.ReceiveTimeout = TimeSpan.FromMinutes(5);
            httpBinding.OpenTimeout = TimeSpan.FromMinutes(10);
            System.ServiceModel.EndpointAddress endPoint = new System.ServiceModel.EndpointAddress(address);
            SharePoint.ListsService.ListsSoapClient rvalue = new SharePoint.ListsService.ListsSoapClient(httpBinding, endPoint);
            return rvalue;
        }

        private List<XElement> CreateUpdateAndInsertBody(List<List<KeyValuePair<int, List<MappedValue>>>> updateGroups)
        {
            List<XElement> rvalue = new List<XElement>();
            foreach (var update in updateGroups)
            {
                rvalue.Add(GetUpdateXml(update, true, dupAction));
            }
            return rvalue;
        }

        private void CreateUpdateListsSoapClient()
        {
            this.client = CreateSoapClient();
            client.UpdateListItemsCompleted += client_UpdateListItemsCompleted;
        }

        void deleteClient_UpdateListItemsCompleted(object sender, SharePoint.ListsService.UpdateListItemsCompletedEventArgs e)
        {
            if (splitDeletes != null && currentDeleteCounter < totalDeleteBatches)
            {
                currentDeleteCounter++;
                int indexes = splitDeletes.Count - 1;
                if (indexes >= currentDeleteCounter)
                {
                    ProcessDeleteBatch(splitDeletes[currentDeleteCounter]);
                }

            }
            else
            {
                ProcessComplete(true, "CSV File Import Completed and Duplicate Records Removed");
            }
        }

        private object ExternalLookupReplace(object p, FieldTransformation fieldTransformation, List<MappedValue> allColumns)
        {
            object rvalue = p;
            if (fieldTransformation != null && !string.IsNullOrEmpty(fieldTransformation.TransformRule) && !string.IsNullOrEmpty(fieldTransformation.TransformValue))
            {
                string rule = fieldTransformation.TransformRule;
                string value = fieldTransformation.TransformValue;
                if (!string.IsNullOrWhiteSpace(rule))
                {
                    string web = SharePointCSVImporter.RuleEditors.ExternalLookupVM.ExternalLookupTranformParser.GetUrlFromTranformRule(rule);
                    string listName = SharePointCSVImporter.RuleEditors.ExternalLookupVM.ExternalLookupTranformParser.GetListNameFromTransformRule(rule);
                    bool useFirstIfMulti = SharePointCSVImporter.RuleEditors.ExternalLookupVM.ExternalLookupTranformParser.GetMultiSettingsFromRule(rule);
                    bool BlankIfNull = SharePointCSVImporter.RuleEditors.ExternalLookupVM.ExternalLookupTranformParser.GetNullSettingsFromRule(rule);
                    string targetField = SharePointCSVImporter.RuleEditors.ExternalLookupVM.ExternalLookupTranformParser.GetTargetFieldFromRule(rule);

                    Dictionary<string, string> filterFields = new Dictionary<string, string>();
                    if (!string.IsNullOrEmpty(value) && allColumns != null)
                    {
                        int nFilters = SharePointCSVImporter.RuleEditors.ExternalLookupVM.ExternalLookupTranformParser.GetNumberOfFiltersFromValue(value);
                        if (nFilters > 0)
                        {
                            string FieldName1 = SharePointCSVImporter.RuleEditors.ExternalLookupVM.ExternalLookupTranformParser.GetFieldName1FromValue(value);
                            string CsvColumn1 = SharePointCSVImporter.RuleEditors.ExternalLookupVM.ExternalLookupTranformParser.GetCsvColumn1FromValue(value);
                            string filterValue = GetCsvColumnValue(allColumns, CsvColumn1);
                            filterFields.Add(FieldName1, filterValue);
                            if (nFilters > 1)
                            {
                                string FieldName2 = SharePointCSVImporter.RuleEditors.ExternalLookupVM.ExternalLookupTranformParser.GetFieldName2FromValue(value);
                                string CsvColumn2 = SharePointCSVImporter.RuleEditors.ExternalLookupVM.ExternalLookupTranformParser.GetCsvColumn2FromValue(value);
                                string filterValue2 = GetCsvColumnValue(allColumns, CsvColumn2);
                                filterFields.Add(FieldName2, filterValue2);
                                if (nFilters > 2)
                                {
                                    string FieldName3 = SharePointCSVImporter.RuleEditors.ExternalLookupVM.ExternalLookupTranformParser.GetFieldName3FromValues(value);
                                    string CsvColumn3 = SharePointCSVImporter.RuleEditors.ExternalLookupVM.ExternalLookupTranformParser.GetCsvColumn3FromValue(value);
                                    string filterValue3 = GetCsvColumnValue(allColumns, CsvColumn3);
                                    filterFields.Add(FieldName3, filterValue3);
                                }
                            }
                        }
                        string transformedValue = rvalue.ToString();
                        if (this.externalListWorker != null && this.externalListWorker.ExternallookupItems != null)
                        {
                            this.externalListWorker.ItemExists(filterFields, targetField, useFirstIfMulti, BlankIfNull, p.ToString(), this.externalListWorker.ExternallookupItems, out transformedValue);
                        }
                        rvalue = transformedValue;
                    }

                }
            }
            return rvalue;
        }

        private object formatValueToType(object value, string typeString)
        {
            object rvalue = value;
            switch (typeString)
            {
                case "Microsoft.SharePoint.Client.FieldText":
                    rvalue = value.ToString();
                    break;
                case "Microsoft.SharePoint.Client.FieldNumber":
                    double dblValue = 0;
                    if (double.TryParse(value.ToString(), out dblValue))
                    {
                        rvalue = dblValue;
                    }
                    break;
                case "Microsoft.SharePoint.Client.FieldUrl":
                    if (value != null)
                    {
                        string[] parts = value.ToString().Split(',');
                        if (parts.Length > 0)
                        {
                            try
                            {
                                Microsoft.SharePoint.Client.FieldUrlValue fv = new FieldUrlValue();
                                fv.Url = parts[0];
                                fv.Description = parts[1];
                                rvalue = fv.ToString();
                            }
                            catch { }
                        }
                    }
                    break;
                case "Microsoft.SharePoint.Client.FieldCurrency":
                    decimal decValue = 0;
                    if (decimal.TryParse(value.ToString(), out decValue))
                    {
                        rvalue = decValue;
                    }
                    break;
                case "Microsoft.SharePoint.Client.FieldDateTime":
                    DateTime dtvalue = DateTime.MinValue;
                    if (DateTime.TryParse(value.ToString(), out dtvalue))
                    {
                        rvalue = dtvalue.ToString("yyyy-MM-ddThh:mm:ssZ");
                    }
                    break;
                default:
                    rvalue = value;
                    break;
            }

            return rvalue;
        }

        private void GetContext(string webUrl)
        {
            thisContext = ClientContext.Current;
            if (thisContext == null)
            {
                thisContext = new ClientContext(webUrl);

                disposeContext = true;
            }
        }

        private string GetCsvColumnValue(List<MappedValue> allColumns, string CsvColumn1)
        {
            string rvalue = "";
            try
            {
                rvalue = allColumns.FirstOrDefault(c => c.FieldMapping.CsvColumnName.Equals(CsvColumn1)).Value.ToString();
            }
            catch { }
            return rvalue;
        }

        private void GetExternalLookupItemsThenContinue()
        {
            var externalLookupConfig = (from c in _mappedData
                                        from d in c.Value
                                        where d.FieldMapping.Transformation != null && d.FieldMapping.Transformation.TransformationType.Equals(TransformationType.ExternalLookupReplace)
                                        select d.FieldMapping.Transformation).FirstOrDefault();
            string rule = externalLookupConfig.TransformRule;
            string web = SharePointCSVImporter.RuleEditors.ExternalLookupVM.ExternalLookupTranformParser.GetUrlFromTranformRule(rule);
            string listName = SharePointCSVImporter.RuleEditors.ExternalLookupVM.ExternalLookupTranformParser.GetListNameFromTransformRule(rule);

            this.externalListWorker = new ExternalListLookupWorker();
            this.externalListWorker.GetExternalLookupItems(web, listName, ExternalLookupItemsReceived);
        }

        private void GetTargetItemsPagedThenStartUpdates()
        {
            if (targetList != null)
            {
                if (currentPage == 0)
                {
                    progressUpdate("Reading existing records: 0/" + existingItemCount.ToString(), 7);
                    currentPage = 1;
                }
                else
                {
                    if (existingItems != null)
                    {
                        int downloaded = existingItems.Count;
                        double perc = ((double)downloaded / (double)existingItemCount);
                        //max is 50% and started with 7& so 43% available
                        perc = Math.Round(perc, 2);
                        int newPercentComplete = (int)Math.Ceiling(7 + (perc * 43));
                        progressUpdate("Reading existing records: " + downloaded.ToString() + "/" + existingItemCount.ToString(), newPercentComplete);
                    }

                }

                CamlQuery qry = CamlQuery.CreateAllItemsQuery(itemLimit);
                qry.ListItemCollectionPosition = itemPosition;
                currentItems = targetList.GetItems(qry);
                thisContext.Load(currentItems);
                thisContext.ExecuteQueryAsync(onGetItemsSucceeded, OnGetItemsFailed);
            }
        }

        private void GetTargetItemsToPossiblyOverwriteThenStartUpdates()
        {
            Web web = thisContext.Web;
            ListCollection lists = web.Lists;
            targetList = lists.GetByTitle(listName);
            thisContext.Load(targetList);
            thisContext.ExecuteQueryAsync(OnGetListInfoSucceeded, OnGetItemsFailed);
        }

        private object GetTransformedValue(MappedValue column, List<MappedValue> allColumns)
        {
            object rvalue = column.Value;
            if (column.FieldMapping.Transformation != null)
            {
                switch (column.FieldMapping.Transformation.TransformationType)
                {
                    case TransformationType.StringReplace:
                        rvalue = column.Value.ToString().Replace(column.FieldMapping.Transformation.TransformRule, column.FieldMapping.Transformation.TransformValue);
                        break;
                    case TransformationType.LookupReplace:
                        rvalue = LookupReplace(column.Value, column.FieldMapping.Transformation);
                        break;
                    case TransformationType.ExternalLookupReplace:
                        rvalue = ExternalLookupReplace(column.Value, column.FieldMapping.Transformation, allColumns);
                        break;
                    default:
                        break;
                }
            }
            return rvalue;
        }

        //duplicate Actions: 0 is do nothing 1 is update latest 2 is update latest and delete others
        private XElement GetUpdateXml(List<KeyValuePair<int, List<MappedValue>>> mappedData, bool checkExistingData, int duplicateAction)
        {
            XElement rvalue = new XElement("Batch");
            rvalue.SetAttributeValue("ListVersion", 1);
            rvalue.SetAttributeValue("OnError", "Continue");
            foreach (var record in mappedData)
            {

                bool addRecord = true;
                int[] existingIds = null;
                bool recordExists = false;
                if (checkExistingData)
                {
                    recordExists = RecordExists(record.Value, out existingIds);
                    if (recordExists && dupAction == 0)
                    {
                        addRecord = false;
                    }
                }

                if (addRecord)
                {
                    XElement recordElement = new XElement("Method");
                    XElement idFieldElement = new XElement("Field");
                    idFieldElement.SetAttributeValue("Name", "ID");
                    recordElement.SetAttributeValue("ID", record.Key);
                    if (checkExistingData)
                    {

                        if (recordExists)
                        {
                            recordElement.SetAttributeValue("Cmd", "Update");
                            idFieldElement.SetValue(existingIds[0]);
                            if (existingIds.Length > 1 && duplicateAction.Equals(2))
                            {
                                for (var i = 1; i < existingIds.Length; i++)
                                {
                                    UpdateItemsToDeleteQueue(existingIds[i]);
                                }
                            }
                        }
                        else
                        {
                            recordElement.SetAttributeValue("Cmd", "New");
                            idFieldElement.SetValue("New");
                        }
                    }
                    else
                    {
                        recordElement.SetAttributeValue("Cmd", "New");
                        idFieldElement.SetValue("New");
                    }
                    recordElement.Add(idFieldElement);
                    foreach (var column in record.Value)
                    {
                        if (!string.IsNullOrEmpty(column.FieldMapping.DestinationColumn) && column.Value != null && !string.IsNullOrEmpty(column.Value.ToString()))
                        {
                            if (column.FieldMapping.Transformation != null)
                            {
                                column.Value = GetTransformedValue(column, record.Value);
                            }
                            XElement fieldElement = new XElement("Field");
                            fieldElement.SetAttributeValue("Name", column.FieldMapping.DestinationColumn);
                            object value = formatValueToType(column.Value, column.FieldMapping.DestinationColumnType);
                            fieldElement.SetValue(value);
                            recordElement.Add(fieldElement);
                        }
                    }
                    rvalue.Add(recordElement);
                }
            }
            return rvalue;
        }

        private bool keyValueExists(ListItem c, string column, string columnType, object value)
        {
            bool rvalue = false;
            try
            {
                object cValue = c[column];
                object cfValue = formatValueToType(cValue, columnType);
                object evalue = formatValueToType(value, columnType);
                //if there is no time in the value - ignore time
                if (columnType.Equals("Microsoft.SharePoint.Client.FieldDateTime"))
                {
                    if (evalue.ToString().Contains("T12:00:00"))
                    {
                        cfValue = cfValue.ToString().Split('T')[0];
                        evalue = evalue.ToString().Split('T')[0];
                    }
                }
                rvalue = cfValue.Equals(evalue);
            }
            catch { }

            return rvalue;
        }

        private object LookupReplace(object p, FieldTransformation fieldTransformation)
        {
            object rvalue = p;
            List<string> lookups = new List<string>();
            if (fieldTransformation != null && !string.IsNullOrEmpty(fieldTransformation.TransformValue))
            {
                string transformValue = fieldTransformation.TransformValue.Replace("\"", "");
                if (fieldTransformation.TransformValue.Contains("|"))
                {
                    lookups = fieldTransformation.TransformValue.Split('|').ToList();
                }
                else
                {
                    lookups.Add(fieldTransformation.TransformValue);
                }
                try
                {
                    rvalue = lookups.Where(c => c.Split(':')[0].Equals(p.ToString())).Select(c => c.Split(':')[1]);
                }
                catch (Exception ex)
                {
                    rvalue = p;
                }
            }
            return rvalue;
        }

        private void OnGetFieldsSucceeded(Object sender, ClientRequestSucceededEventArgs args)
        {
            ReceivedFieldsAction(fields.ToList());
        }

        private void OnGetItemsFailed(object sender, ClientRequestFailedEventArgs args)
        {
            ErrorAction("An error occurred when reading existing records/r/n" + args.ErrorDetails);
        }

        private void onGetItemsSucceeded(Object sender, ClientRequestSucceededEventArgs args)
        {
            if (existingItems == null)
            {
                existingItems = new List<ListItem>();
            }
            itemPosition = currentItems.ListItemCollectionPosition;
            if (currentItems != null)
            {

                existingItems.AddRange(currentItems.ToList());
            }
            if (currentPage.Equals(pageCount))
            {
                ListItemEqualityComparer itemCmp = new ListItemEqualityComparer();
                existingItems = existingItems.Distinct(itemCmp).ToList();

                StartUpdates();
            }
            else
            {
                currentPage++;
                GetTargetItemsPagedThenStartUpdates();
            }
        }

        private void OnGetListFailed(object sender, ClientRequestFailedEventArgs args)
        {
            ErrorAction("An error Occurred.  Continue?/r/n/" + args.Message);
        }

        private void OnGetListInfoSucceeded(Object sender, ClientRequestSucceededEventArgs args)
        {
            existingItemCount = targetList.ItemCount;

            if (existingItemCount >= itemLimit)
            {
                pageCount = (int)Math.Ceiling((double)existingItemCount / (double)itemLimit);
            }
            else
            {
                pageCount = 1;
            }
            if (existingItemCount > 0)
            {
                GetTargetItemsPagedThenStartUpdates();
            }
            else
            {
                StartUpdates();
            }
        }

        private void OnGetListSucceeded(Object sender, ClientRequestSucceededEventArgs args)
        {
            ReceiveListsAction(lists.ToList());
        }

        private void ProcessComplete(bool success, string message)
        {
            if (!processCompleteCalled)
            {
                processCompleteCalled = true;
                UpdateCompletedAction(true, message);
                if (loggingTask != null && loggingTask.Status == TaskStatus.Running)
                {
                    loggingTask.Wait();
                }
                FinializeProcess(logger);
                this.Dispose();
            }
        }

        private void ProcessDeleteBatch(List<KeyValuePair<int, XElement>> itemToDeleteMethods)
        {
            XElement batch = new XElement("Batch");
            batch.SetAttributeValue("ListVersion", 1);
            batch.SetAttributeValue("OnError", "Continue");
            foreach (var record in itemToDeleteMethods)
            {
                if (record.Value != null)
                {
                    batch.Add(record.Value);
                }

                Deployment.Current.Dispatcher.BeginInvoke(delegate()
                {
                    if (deleteClient == null)
                    {
                        deleteClient = CreateSoapClient();
                        deleteClient.UpdateListItemsCompleted += deleteClient_UpdateListItemsCompleted;
                    }
                    deleteClient.UpdateListItemsAsync(this.listName, batch);

                });
            }
        }

        private void SearchCatchErrors(XElement updateResponse, Exception exception, XElement updatesSent)
        {
            if (exception == null && updateResponse != null)
            {
                if (loggingTask!=null && loggingTask.Status == TaskStatus.Running)
                {
                    loggingTask.Wait();
                }
                loggingTask = Task.Factory.StartNew(delegate()
                {
                    logger.LogUpdateErrors(updateResponse, updatesSent);
                });
            }
        }

        private void StartDeletesThenFinish()
        {
            if (itemToDeleteMethods != null && itemToDeleteMethods.Count > 0)
            {
                progressUpdate("Deleting " + itemToDeleteMethods.Count + " duplicate records (option was selected)", 98);
                if (itemToDeleteMethods.Count > 150)
                {
                    //need to split operations
                    splitDeletes = SplitDeleteOperations(itemToDeleteMethods.ToList(), 150);
                    this.totalDeleteBatches = splitDeletes.Count;
                    ProcessDeleteBatch(splitDeletes[currentDeleteCounter]);
                }
                else
                {
                    splitDeletes = null;
                    totalDeleteBatches = 1;
                    ProcessDeleteBatch(itemToDeleteMethods.ToList());
                }
            }
            else
            {
                ProcessComplete(true, "CSV File Import Completed");
            }
        }

        private void StartStep2()
        {
            if (_overwrite)
            {
                GetTargetItemsToPossiblyOverwriteThenStartUpdates();
            }
            else
            {
                StartUpdates();
            }
        }

        private void StartUpdates()
        {
            if (_mappedData != null)
            {
                List<List<KeyValuePair<int, List<MappedValue>>>> updateGroups = SplitUpdates(_mappedData, 150);

                progressUpdate("Preparing Batch Updates.  This CSV will be broken into: " + updateGroups.Count + " batches.", 55);
                System.Threading.Thread.Sleep(500);

                if (updateGroups != null && updateGroups.Count > 0)
                {
                    if (currentItems == null || currentItems.Count < 1)
                    {
                        pendingBatchUpdates = CreateInsertOnlyBody(updateGroups);
                    }
                    else
                    {
                        pendingBatchUpdates = CreateUpdateAndInsertBody(updateGroups);
                    }
                }
                CreateUpdateListsSoapClient();
                progressUpdate("Uploading CSV data to SharePoint List, " + this.listName, 60);
                UpdateTarget(pendingBatchUpdates[updateIndex]);
            }
        }

        private void UpdateTarget(System.Xml.Linq.XElement updateMessage)
        {
            Deployment.Current.Dispatcher.BeginInvoke(delegate()
               {
                   System.Threading.Thread.Sleep(1000);
                   client.UpdateListItemsAsync(this.listName, updateMessage);
               });
        }

        #endregion Methods

        #region Nested Types

        private class ListItemEqualityComparer : IEqualityComparer<ListItem>
        {
            #region Methods

            public bool Equals(ListItem i1, ListItem i2)
            {
                if (i1.Id.Equals(i2.Id))
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }

            public int GetHashCode(ListItem obj)
            {
                return obj.Id;
            }

            #endregion Methods
        }

        #endregion Nested Types
    }
}